#include "lom-instance.hh"
#include "lom-errors.hh"
#include "lom-value.hh"
#include "lom-property.hh"
#include "lom-class.hh"

using namespace licada;

namespace lom
{
    
    LomInstance::LomInstance() : cls( NULL ), factory( NULL )
    {
    }
    LomInstance::LomInstance( const LomClass* _cls
			      , ILomValueFactory* _factory
			      , ILomInstanceManager* _mgr
			      , THandle reserveHandle )
	: cls(NULL), factory(NULL)
    {
	initialize( _cls, _factory, _mgr, reserveHandle  );
    }
    static inline LomValue* assignValue( const LomValue* other, ILomValueFactory* factory )
    {
	if ( other && factory )
	    return other->clone( factory );
	return NULL;
    }
    
    LomInstance::LomInstance( const LomInstance& other
		 , ILomValueFactory* factory
		 , ILomInstanceManager* mgr
		 , THandle reserveHandle )
    {
	initialize( other.getClass(), factory, mgr, reserveHandle );
	std::transform( other.getValues().begin(), other.getValues().end()
			, values.begin(), bind( assignValue, _1, factory ) );
    }

    LomInstance::~LomInstance() { initialize( NULL, NULL, NULL ); }

    const LomClass* LomInstance::getClass() const { return cls; }
    
    void LomInstance::setClass( const LomClass* _cls )
    {
	cls = _cls;
    }
    
    void LomInstance::setFactory( ILomValueFactory* _factory ) {
	factory = _factory;
    }

    LomValue* transformMethod( const LomValue* input
			       , const LomProperty& property
			       , ILomValueFactory* factory  )
    {
	if ( input == NULL )
	    return factory->create(property.type);
	return input->clone(factory);
    }
    
    void deleteValue( LomValue* value, ILomValueFactory* factory ) 
    {
	if ( value ) {
	    if ( factory )
		factory->destroy( value );
	    else
		delete value; 
	}
    }
    void LomInstance::initialize( const LomClass* _cls
				  , ILomValueFactory* _factory
				  , ILomInstanceManager* _mgr
				  , THandle reserveHandle )
    {
	LomRoot::initializeLomRoot( _mgr, reserveHandle );
	std::for_each( values.begin(), values.end(), boost::bind( deleteValue, _1, factory ) );
	values.clear();
	cls = _cls;
	factory = _factory;
	if ( cls ) {
	    const TPropertyVector& props = cls->getProperties();
	    const TValueVector& defaults = cls->getDefaults();
	    values.resize(props.size());
	    std::transform( defaults.begin()
			    , defaults.end()
			    , props.begin()
			    , values.begin()
			    , boost::bind( transformMethod, _1, _2, factory) );
	}
    }

    const LomValue& LomInstance::get( const licada::TNameStr& name ) const
    {
	int32 index = cls->getPropertyIndex(name, true);
	return getIdx( index );
    }
    
    const LomValue& LomInstance::getIdx( licada::uint32 index ) const
    {
	return *values.at(index);
    }
    
    void LomInstance::set( const licada::TNameStr& name, const LomValue& value )
    {
	int32 index = cls->getPropertyIndex( name );
	setIdx( index, value );
    }
    
    void LomInstance::setIdx( licada::uint32 index, const LomValue& value )
    {
	*(values.at(index)) = value;
	sendPropertyEvent( handle, index, *(values.at(index)) );
    }

    const TValueVector& LomInstance::getValues() const
    {
	return values;
    }
    
    TValueVector& LomInstance::getValues()
    {
	return values;
    }
}
